#include<unistd.h>
#include<sys/types.h>
#include<sys/sem.h>
#include<sys/mman.h>
#include<sys/ipc.h>
#include<stdio.h>
#include<stdlib.h>

int* critical;
const int sizeofbuffer = 5;
int in = 0;
int out = 0;

int createSem() {
	return semget((key_t)1000, 3, IPC_CREAT | 0600);
}

void initSignal(int semid, int index, int value) {
	semctl(semid, index, SETVAL, value);
}

void make_critical() {
	critical = mmap(NULL, 5 * sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

}

void semWait(int semid, int index) {
	struct sembuf sem_buf;
	sem_buf.sem_num = index;
	sem_buf.sem_op = -1;
	sem_buf.sem_flg = SEM_UNDO;
	semop(semid, &sem_buf, 1);
}

void semSignal(int semid, int index) {
	struct sembuf sem_buf;
	sem_buf.sem_num = index;
	sem_buf.sem_op = 1;
	sem_buf.sem_flg = SEM_UNDO;
	semop(semid, &sem_buf, 1);
}


void producer(int semid) {
	while (1) {
		semWait(semid, 2);
		semWait(semid, 0);

		critical[in] = rand() % 10;
		printf("Producer: produce int %d at %d position.\n", critical[in], in);
		sleep(rand() % 3);
		in = (in + 1) % sizeofbuffer;

		semSignal(semid, 0);
		semSignal(semid, 1);
	}
}

void consumer(int semid) {
	while (1) {
		semWait(semid, 1);
		semWait(semid, 0);

		printf("Consumer: consumer int %d at %d position.\n", critical[out], out);
		sleep(rand() % 3);
		out = (out + 1) % sizeofbuffer;

		semSignal(semid, 0);
		semSignal(semid, 2);
	}
}

int main() {
	int semid = createSem();
	initSignal(semid, 0, 1);
	initSignal(semid, 1, 0);
	initSignal(semid, 2, sizeofbuffer);
	make_critical();

	pid_t child = fork();
	if (child < 0) {
		printf("fail");
	}
	else if (child == 0)
	{
		consumer(semid);
	}
	else
	{
		producer(semid);
	}
	return 0;
}